package com.app.utils;

import com.app.pojo.FileInfo;
import com.app.pojo.MyFile;
import com.app.pojo.MyPath;
import com.app.pojo.Status;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Transaction;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 在redis中，文件的存储格式为：
 *  文件夹以 path 为 key ，值为一个 list，list中存放文件夹下的文件和目录
 *  文件：
 *      存在list中，格式为 ”文件名"
 *      键值保存， 键为 path ，值为："文件名|md5|最后修改时间|文件大小“
 *  目录：
 *      键值保存， 以 path 为 key ，值为一个 list，list中存放目录下的文件和目录
 *      存在list中，格式为 ”/文件夹名“
 *  注：list第一项为 ":目录名|目录创建时间“
 */
@Component//单例设计模式
public class FileManager {
    JedisPool jedisPool;

    public final String DIR_FLAG="/";
    public FileManager() {
        this.jedisPool = RedisUtils.getJedisPool();
    }
    public Jedis getJedis(){
        return jedisPool.getResource();
    }
    public boolean mkUser(String userId){
        if (!Judges.judgeNumber(userId)) return false;
//        System.out.println("mkUser :"+jedisPool.getResource());/***********打印***********/
        try (Jedis jedis = jedisPool.getResource()) {
            if (jedis.exists(userId)){
                deleteAll(new MyFile(userId));
                Transaction multi = jedis.multi();//使用事物
                multi.rpush(userId,":" + new Date().getTime());
                multi.exec();
            }else {
                jedis.rpush(userId,":" + userId + "|" + new Date().getTime());
            }
            return true;
        }
    }
    public boolean mkDir(MyPath path,String dirname) {
        if (path==null||!path.isAvailable()||!Judges.judgeDirname(dirname)) return false;
        String dir=path.getSourcePath();
//        System.out.println("mkdir :"+dir+"/"+dirname);/***********打印***********/
        try (Jedis jedis = jedisPool.getResource()) {
            if (jedis.exists(dir+"/"+dirname))return false;
            if (jedis.exists(dir)) {
                Transaction multi = jedis.multi();//使用事物
                multi.rpush(dir+"/"+dirname, ":"  + new Date().getTime());
                multi.rpush(dir,"/"+dirname);
                multi.exec();
                return true;
            }
        }
        return false;
    }
    public boolean mkDir(MyPath pathname) {
        return mkDir(pathname.getParentPath(),pathname.getDirname());
    }
    public boolean mkDirs(MyPath path){
        if (path==null||!path.isAvailable()) return false;
//        System.out.println("mkdirs :"+path.getSourcePath());/***********打印***********/
        try (Jedis jedis = jedisPool.getResource()) {
            String[] dirnames=path.getSourcePath().split("/");
            String root=dirnames[0];
            Transaction multi = jedis.multi();//使用事物
            boolean flag=false;
            long data=new Date().getTime();
            for (int i=1 ;i<dirnames.length;++i){
                if (flag||!jedis.exists(root+"/"+dirnames[i])){
                    multi.rpush(root+"/"+dirnames[i], ":" + dirnames[i] + "|" + + data);
                    multi.rpush(root,"/"+dirnames[i]);
                    flag=true;
                }
                root+="/"+dirnames[i];
            }
            multi.exec();
            return true;
        }
    }
    //递归用 jedis用来判断
    private boolean mkDirs(MyPath path,Jedis jedis){
        if (path==null) return false;
        MyPath oldPath = path.getParentPath();
        String dirname= path.getDirname();
        if (jedis.exists(oldPath.getSourcePath()))
            return mkDir(oldPath,dirname);
        else {
            return mkDirs(oldPath,jedis);
        }
    }

    /**
     * 创建虚拟文件，会以全目录为键，文件信息为值存储。
     * 并将文件名放在以文件父目录为键的列表list中、
     */
    public boolean creatFile(MyFile myFile){
        if (myFile==null||!myFile.isAvailable()) return false;
//        if (!myFile.getRealFile(Settings.rootsDir).isFile())return false;//md5不存在
//        System.out.println("creatFile :"+myFile);/***********打印***********/
        try (Jedis jedis = jedisPool.getResource()) {
            if (jedis.exists(myFile.getAbsolutePath()))return false;//已存在
            if (jedis.exists(myFile.getDirectory())) {//看父目录存在不
                Transaction multi = jedis.multi();//使用事物
                multi.rpush(myFile.getDirectory(),myFile.getName());
                multi.set(myFile.getAbsolutePath(),myFile.getInfoStr());
                multi.exec();
                return true;
            }
            else System.out.println("出现错误或父目录不存在");
        }
        return false;
    }
    /**
     * 删除虚拟文件，如果删除的是目录，则连同目录中的所有文件都删除
     *  会以全目录为键删除。
     * 并删除将以 文件父目录 为键的列表 list 中的 改文件名、
     * 注意：不能删除根目录！
     */
    public boolean deleteAll(MyFile myFile){
        if (myFile==null||myFile.getName()==null||!myFile.isAvailable()) return false;
//        System.out.println("delete :"+myFile);/***********打印***********/
        try (Jedis jedis = jedisPool.getResource()) {
            if (jedis.exists(myFile.getAbsolutePath()))
                doDelAll(myFile.getDirectory(),myFile.getName(),jedis);
            else return false;
            return true;
        }
    }
    /**
     * 删除虚拟文件，如果删除的是目录且不为空，则删除失败
     *  会以全目录为键删除。
     * 并删除将以 文件父目录 为键的列表 list 中的 改文件名、
     * 注意：不能删除根目录！
     */
    public boolean delete(MyFile myFile){
        if (myFile==null||myFile.getName()==null||!myFile.isAvailable()) return false;
//        System.out.println("deleteAll :"+myFile);/***********打印***********/
        try (Jedis jedis = jedisPool.getResource()) {
            String type = jedis.type(myFile.getAbsolutePath());
            String flag;
            //是文件还是文件夹
            if ("string".equals(type)) flag="";
            else if ("list".equals(type)){//是文件夹，文件夹是否为空
                if (jedis.lrange(myFile.getDirectory(), 0,1).size()>1)
                    return false;
                flag=DIR_FLAG;
            }else return false;//不存在
            //删除
            Transaction multi = jedis.multi();//使用事物
            multi.lrem(myFile.getDirectory(), 0,flag+myFile.getName());
            multi.del(myFile.getAbsolutePath());
            multi.exec();
            multi.close();
            return true;
        }
    }

    //递归删除
    private void doDelAll(String dir, String filename,Jedis jedis){
        String pathname=dir+"/"+filename;
        String type = jedis.type(pathname);
        //删除的是目录
        if ("list".equals(type)){
            List<String> li = jedis.lrange(pathname,1,-1);
            if (li.size()>0){//目录中有文件
                for (String child : li)//先删除目录下所有文件
                    doDelAll(pathname,child.replace(DIR_FLAG,""),jedis);
            }
        }
        String flag = "string".equals(type)? "":DIR_FLAG;
        //删除当前
        Transaction multi = jedis.multi();//使用事物
        multi.lrem(dir,0,flag+filename);
        multi.del(pathname);
        multi.exec();
        multi.close();
    }
    public String delFiles(List<MyFile> files){
        StringBuffer res = new StringBuffer();
//        System.out.println("delFiles :"+files);/***********打印***********/
       //不使用事物
        try(Jedis jedis = jedisPool.getResource()) {
            for (MyFile file:files){
                if (jedis.exists(file.getAbsolutePath())){
                    doDelAll(file.getDirectory(),file.getName(),jedis);
                    res.append(1);
                } else res.append(0);
            }
        }
        return res.toString();
    }

    /**
     * 重命名到新文件
     * 注意：根目录不能重命名！即 userId 不能在此修改
     * @param source 源目录
     * @param target 目标文件，注意是文件不是目标目录
     * @return INPUT_NOT_REGULATED(路径问题) FILE_ALREADY_EXIST FILE_NOT_FOUND（文件不存在或目标目录不存在） SYSTEM_ERROR SUCCESS
     */
    public int renameToMyFile(MyFile source,MyFile target){
        if (source.getName()==null||target.getName()==null||!source.isAvailable()||!target.isAvailable()) return Status.INPUT_NOT_REGULATED;
//        System.out.println("renameTo :"+source+" to "+target);/***********打印***********/
        try (Jedis jedis = jedisPool.getResource()) {
            //源存在，目标目录存在，目标文件不存在
            String sourcePathname=source.getAbsolutePath();
            if (jedis.exists(target.getAbsolutePath()) ) return Status.FILE_ALREADY_EXIST;
            if (!"list".equals(jedis.type(target.getDirectory()))) return Status.FILE_DIR_NOT_FOUND;

            String type = jedis.type(sourcePathname);
            if ("null".equals(type)) return Status.FILE_NOT_FOUND;
            String flag = "string".equals(type)?"":DIR_FLAG;

            Transaction multi = jedis.multi();//使用事物
            multi.lrem(source.getDirectory(),0,flag+source.getName());//删除原来的
            multi.rpush(target.getDirectory(),flag+target.getName());//添加新的
//            multi.rename(sourcePathname,target.getAbsolutePath());//文件移动
            multi.exec();
            //更改键
            //是目录的话，需要更改目录下所有文件的键
            if ("list".equals(type)) {
                renameFlashPaths(sourcePathname,target.getAbsolutePath(),jedis);
            }else jedis.rename(sourcePathname,target.getAbsolutePath());
            return Status.SUCCESS;
        }catch (Exception e){e.printStackTrace();}
        return Status.SYSTEM_ERROR;
    }
    //递归重命名后的子文件路径
    private void renameFlashPaths(String oldPath,String newPath,Jedis jedis){
        List<String> childes=jedis.lrange(oldPath,1,-1);
        for (String name:childes){//更改目录下子文件
            if (name.startsWith("/")) renameFlashPaths(oldPath+name,newPath+name,jedis);
            else jedis.rename(oldPath+"/"+name,newPath+"/"+name);
        }
        jedis.rename(oldPath,newPath);//更改自己
    }
    //递归复制文件夹地址,
    private void copyDirPaths(String sourcePathname,String targetPathname,Jedis jedis){
        //添加到目的文件夹列表
        //复制路径
        List<String> li=jedis.lrange(sourcePathname,0,-1);
        String[] arr = new String[li.size()];
        int i=0;
        for (String sub:li) arr[i++]=sub;
        jedis.rpush(targetPathname,arr);

        List<String> childes=jedis.lrange(sourcePathname,1,-1);
        for (String child:childes){//更改目录下的子文件
            if (child.startsWith("/"))//文件夹
                copyDirPaths(sourcePathname+child,targetPathname+child,jedis);
            else //文件
                jedis.set(targetPathname+"/"+child,jedis.get(sourcePathname+"/"+child));
        }
    }
    /**
     *
     * @param source 源文件
     * @param targetDir 目标文件夹
     * @return
     */
    public int copyToDir(MyFile source,MyPath targetDir){
        if (source.getName()==null||!source.isAvailable()||!targetDir.isAvailable()) return Status.INPUT_NOT_REGULATED;
//        System.out.println("copy :"+source+" to "+targetDir);/***********打印***********/
        try (Jedis jedis = jedisPool.getResource()) {
            //源存在，目标文件不存在，目标父目录存在且为目录
            String targetPath=targetDir.getSourcePath();
            String targetPathname=targetPath+"/"+source.getName();
            String sourcePathname=source.getAbsolutePath();
            if (!"list".equals(jedis.type(targetPath))) return Status.FILE_DIR_NOT_FOUND;
            if (jedis.exists(targetPathname)) return Status.FILE_ALREADY_EXIST;
            String type = jedis.type(sourcePathname);
            if ("null".equals(type)) return Status.FILE_NOT_FOUND;
            if ("list".equals(type)){
                jedis.rpush(targetPath,DIR_FLAG+source.getName());
                copyDirPaths(sourcePathname,targetPathname,jedis);
            } else{//文件
                jedis.rpush(targetPath,source.getName());
                jedis.set(targetPathname,jedis.get(sourcePathname));
            }
            return Status.SUCCESS;
        }catch (Exception e){e.printStackTrace();}
        return Status.SYSTEM_ERROR;
    }

    /**
     *
     * @param path
     * @return 没有则返回 null
     */
    public ArrayList<FileInfo> getFileInfoList(MyPath path){
        ArrayList<FileInfo> list=new ArrayList<>();
        if (path==null||!path.isAvailable()) return list;
//        System.out.println("getFileInfoList :"+path);/***********打印***********/
        try (Jedis jedis = jedisPool.getResource()) {
            //源存在，目标目录存在，目标文件不存在
            List<String> names=jedis.lrange(path.getSourcePath(),1,-1);
            for (String name:names){
                FileInfo file;
                //是目录还是文件，查询语句不一样
                if (name.startsWith("/")) file=new FileInfo(name.substring(1),jedis.lrange(path.getSourcePath()+name,0,0).get(0));
                else file = new FileInfo(name,jedis.get(path.getSourcePath()+"/"+name));
                list.add(file);
//                System.out.println(file);
            }
        }catch (Exception e){e.printStackTrace();}
        return list;
    }

    public boolean exist(MyFile file) {
        if (file==null||!file.isAvailable()) return false;
//        System.out.println("exist :"+file);/***********打印***********/
        try (Jedis jedis = jedisPool.getResource()) {
            //源存在，目标目录存在，目标文件不存在\
            return jedis.exists(file.getAbsolutePath());
        }
    }
    public boolean isFile(MyFile file) {
        if (file==null||!file.isAvailable()) return false;
//        System.out.println("isFile :"+file);/***********打印***********/
        try (Jedis jedis = jedisPool.getResource()) {
            //源存在，目标目录存在，目标文件不存在\
            return "string".equals(jedis.type(file.getAbsolutePath()));
        }
    }
    public boolean exist(MyPath path) {
        return exist(new MyFile(path.getSourcePath()));
    }
}
